#include <iostream>
#include <vector>
#include <cstring>
using namespace std;

// 使用三维数组存储记忆化状态：dp[i][j][state]
vector<vector<vector<long long>>> memo;
vector<vector<int>> grid;
int n, m;

long long dfs(int i, int j, int state)
{
	// 边界条件：所有行处理完毕
	if (i == n)
		return 1;

	// 当前行处理完毕，转到下一行第0列
	if (j == m)
		return dfs(i + 1, 0, state);

	// 如果该状态已经计算过，直接返回结果
	if (memo[i][j][state] != -1)
		return memo[i][j][state];

	long long res = 0;

	// 获取上方和左方状态
	int top = (state >> j) & 1; // 上一行同列状态
	int left = (j > 0) ? ((state >> (j - 1)) & 1) : 0; // 当前行左侧状态

	if (grid[i][j] == 0) { // 贫瘠土地，只能不种
		// 将当前位置状态设为0，其他位保持不变
		int new_state = state & ~(1 << j);
		res = dfs(i, j + 1, new_state);
	} else { // 肥沃土地
		// 选择1：不种草
		int new_state = state & ~(1 << j); // 当前位置设为0
		res = dfs(i, j + 1, new_state);

		// 选择2：种草（需检查上方和左方无草）
		if (top == 0 && left == 0) {
			new_state = state | (1 << j); // 当前位置设为1
			res += dfs(i, j + 1, new_state);
		}
	}

	// 存储计算结果并返回
	memo[i][j][state] = res;
	return res;
}

int main()
{
	cin >> n >> m;

	// 读取网格
	grid.resize(n, vector<int>(m));
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			cin >> grid[i][j];
		}
	}

	// 初始化记忆化数组
	memo.resize(n, vector<vector<long long>>(m, vector<long long>(1 << m, -1)));

	// 从左上角开始递归，初始状态全0
	long long result = dfs(0, 0, 0);

	cout << result << endl;
	return 0;
}